package com.netease.nimlib.fts.query;

import java.util.ArrayList;

/**
 * 云信分词算法
 * 
 * @author huangjun 2016-7-8
 */
public class NIMTokenizer {
	private static final boolean OUTPUT_LOG = true;

	private static final int FLAG_NONE = -1;
	private static final int FLAG_ENGLISH = 0; // 英文（连续token）
	private static final int FLAG_CHINESE = 1; // 中文（单个or连续token，看策略）
	private static final int FLAG_NUMBER = 2; // 数字（连续token）
	private static final int FLAG_SYMBOL = 3; // 符号（作为单个token）
	private static final int FLAG_SKIP = 4; // 停词/跳过（例如空格，不作为token）

	private int types[];

	public NIMTokenizer() {
		initTypes();
	}

	private void initTypes() {
		if (types != null) {
			return;
		}

		types = new int[0x80];
		int i = 0;
		for (i = 0x00; i <= 0x20; i++) {
			types[i] = FLAG_SKIP; // 跳过
		}
		for (i = 0x21; i <= 0x2F; i++) {
			types[i] = FLAG_SYMBOL; // 符号
		}
		types[0x2A] = FLAG_SKIP; // * 跳过
		for (i = 0x30; i <= 0x39; i++) {
			types[i] = FLAG_NUMBER; // 数字
		}
		for (i = 0x3A; i <= 0x40; i++) {
			types[i] = FLAG_SYMBOL; // 符号
		}
		for (i = 0x41; i <= 0x5A; i++) {
			types[i] = FLAG_ENGLISH; // 字母
		}
		for (i = 0x5B; i <= 0x60; i++) {
			types[i] = FLAG_SYMBOL; // 符号
		}
		for (i = 0x61; i <= 0x7A; i++) {
			types[i] = FLAG_ENGLISH; // 字母
		}
		for (i = 0x7B; i <= 0x7E; i++) {
			types[i] = FLAG_SYMBOL; // 符号
		}
		types[0x7F] = FLAG_SKIP; // 跳过
	}

	int getCharType(char c) {
		if (c >= 0x80) {
			return FLAG_CHINESE;
		} else {
			return types[c];
		}
	}

	/**
	 * 提取英文或者数字token
	 */
	public ArrayList<String> getEnglishNumberTokens(String input) {
		ArrayList<String> tokens = getTokens(input, false);
		ArrayList<String> result = new ArrayList<>(tokens.size());
		for (String token : tokens) {
			int type = getCharType(token.charAt(0));
			if (type == FLAG_ENGLISH || type == FLAG_NUMBER) {
				result.add(token);
			}
		}

		if (OUTPUT_LOG) {
			System.out.println("---------tokenizer------------");
			System.out.println("get english or number token input:" + input);
			for (String token : result) {
				System.out.println(token);
			}
			System.out.println("---------tokenizer------------");
		}

		return result;
	}

	/**
	 * 分词算法
	 */
	public ArrayList<String> getTokens(String input, boolean singleMode) {
		ArrayList<String> result = new ArrayList<>();
		int p = 0;
		int length = input.length();

		while (p < length) {
			int startOffset = -1; // 新token的起始位置
			char c;
			int flag = FLAG_NONE; // 前面的token是? -1 初始位置 0 英文 1 中文2数字
			// 开始提取新的token
			while (p < length) {
				c = input.charAt(p);
				if (c < 0x80) {
					// 处理ASCII
					int config = types[c];
					if (config == FLAG_ENGLISH) {
						// 处理连续字母
						if (startOffset == -1) {
							startOffset = p; // 确定新的token的起始位置
							flag = FLAG_ENGLISH;
						}
						if (flag == FLAG_CHINESE || flag == FLAG_NUMBER) {
							break; // 如果前面有中文或者数字token，那么先提交前面的token
						}
						p++;
					} else if (config == FLAG_NUMBER) {
						// 处理连续数字
						if (startOffset == -1) {
							startOffset = p; // 确定新的token的起始位置
							flag = FLAG_NUMBER;
						}
						if (flag == FLAG_ENGLISH || flag == FLAG_CHINESE) {
							break; // 如果前面是英文或者中文token，那么先提交前面的token
						}
						p++;
					} else if (config == FLAG_SYMBOL) {
						// 处理单个字符token
						if (startOffset == -1) {
							startOffset = p++; // 确定新的token的起始位置
						}
						break; // 如果前面有token那么先提交前面的token，没有则提交当前单个字符token
					} else {
						// 处理空格等分隔符
						if (startOffset == -1) {
							// 前面没有token，直接处理下一个字符
							p++;
						} else {
							// 前面有token，先提交前面的token
							break;
						}
					}
				} else {
					if (singleMode) {
						// 处理单个中文汉字
						if (startOffset == -1) {
							// 前面没有token，确定新的token位置
							startOffset = p++;
						}
						break; // 如果前面有token那么先提交前面的token，没有则提交当前中文汉字的token
					} else {
						// 处理连续中文汉字
						if (startOffset == -1) {
							startOffset = p; // 确定新的token的起始位置
							flag = FLAG_CHINESE;
						}
						// 如果前面有英文或者数字token那么先提交前面的token
						if (flag == FLAG_ENGLISH || flag == FLAG_NUMBER) {
							break;
						}
						p++;
					}
				}
			}

			// 提交token
			if (startOffset != -1) {
				result.add(new String(input.substring(startOffset, p)));
			}
		}

		// log
		if (OUTPUT_LOG) {
			System.out.println("---------tokenizer------------");
			System.out.println("input:" + input + ",single mode=" + singleMode);
			for (String token : result) {
				System.out.println(token);
			}
			System.out.println("---------tokenizer------------");
		}

		return result;
	}

	/**
	 * ***************** Single instance *****************
	 */
	public static NIMTokenizer getInstance() {
		return InstanceHolder.instance;
	}

	public static class InstanceHolder {
		public final static NIMTokenizer instance = new NIMTokenizer();
	}

	// test
	public static void main(String[] args) {
		String content = "asdfabcd1234黄俊hahaha yeah";
		getInstance().getTokens(content, true);
		getInstance().getTokens(content, false);
		getInstance().getEnglishNumberTokens(content);
	}
}
